package com.talkyun.openx.ocean.base;

import com.talkyun.openx.server.core.InterceptorException;
import com.talkyun.openx.server.error.PageBuilder;
import com.talkyun.openx.server.helper.VersionHelper;
import com.talkyun.openx.server.session.SessionCleaner;
import com.talkyun.openx.server.session.SessionManager;
import com.talkyun.openx.server.timer.RearGuardTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

import static com.talkyun.openx.server.helper.IconHelper.getFavIconBuffer;

// 识别[静态|排除]请求, Session管理

public abstract class BaseHttpFilter implements Filter {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    protected String version = VersionHelper.getOpenxVersion();
    private PageBuilder pageBuilder = PageBuilder.get();

    private String[] excludePath;
    private RearGuardTimer guardTimer;
    private SessionCleaner sessionCleaner;

    private SessionManager sessionManager = SessionManager.get();

    @Override
    public void init(FilterConfig fc) throws ServletException {
        // timer fire
        String timeOutString = getInitParameter(fc, "sessionTimeOut", "openxSessionTimeOut");
        long timeOut = isBlank(timeOutString) ? 30 * 60 * 1000 : Long.parseLong(timeOutString);
        sessionCleaner = new SessionCleaner(timeOut);
        guardTimer = new RearGuardTimer();
        guardTimer.addTask(new Runnable() {
            @Override
            public void run() {
                sessionCleaner.clean();
            }
        }, 30 * 1000); // 30 second, run once
        guardTimer.startup();

        // exclude path init
        String uri = this.getInitParameter(fc, "excludeURI", "openxExcludeURI");
        if (this.isBlank(uri)) {
            excludePath = new String[0];
        } else {
            excludePath = uri.trim().split(",");
            for (int i = 0; i < excludePath.length; i++) {
                excludePath[i] = excludePath[i].trim().toLowerCase();
            }
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain fc) throws IOException, ServletException {
        String uri = ((HttpServletRequest) req).getRequestURI().toLowerCase();
        String method = ((HttpServletRequest) req).getMethod().toLowerCase();

        if (uri.equals("/favicon.ico")) {
            // 1. favicon.ico
            byte[] buf = getFavIconBuffer("/webapp/favicon.ico");
            resp.getOutputStream().write(buf);
        } else if ("head".equals(method) && "/".equals(uri)) {
            // 2. backend monitor: HEAD / HTTP/1.0
            resp.getWriter().println("OK");
        } else if (this.isStaticURI(uri) || this.isExcludeURI(uri)) {
            // 3. static or exclude uri
            sessionManager.updateActiveTime(((HttpServletRequest) req).getSession().getId());
            //    - now to handle request
            try {
                // this.doHttpFilter((HttpServletRequest) req, (HttpServletResponse) resp, fc);
                fc.doFilter(req, resp);
            } catch (InterceptorException e) {
                // response json (include exception)
                pageBuilder.responseJsonPage((HttpServletRequest) req, (HttpServletResponse) resp, e.getCause());
            } catch (Throwable t) {
                // response 500 page
                pageBuilder.responseErrorPage((HttpServletRequest) req, (HttpServletResponse) resp, t);
            }
        } else if ("get".equals(method) || "post".equals(method) || "options".equals(method)) {
            // 4. openx request
            sessionManager.updateActiveTime(((HttpServletRequest) req).getSession().getId());
            //    - now to handle request
            try {
                this.doRestFilter(req, resp, fc);
            } catch (InterceptorException e) {
                logger.error(e.getMessage(), e);
                // response json (include exception)
                pageBuilder.responseJsonPage((HttpServletRequest) req, (HttpServletResponse) resp, e.getCause());
            } catch (Throwable t) {
                logger.error(t.getMessage(), t);
                // response 500 page
                pageBuilder.responseErrorPage((HttpServletRequest) req, (HttpServletResponse) resp, t);
            }
        } else {
            // 5. others PUT,DELETE ...
            fc.doFilter(req, resp);
        }
    }

    protected void doHttpFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain fc) throws IOException, ServletException {
        fc.doFilter(req, resp);
    }

    protected abstract void doRestFilter(ServletRequest req, ServletResponse resp, FilterChain fc) throws IOException;

    @Override
    public void destroy() {
        guardTimer.shutdown();
    }

    private boolean isStaticURI(String uri) {
        String[] ss = {"/", ".ico", ".js", ".css", ".jpg", ".png", ".gif", ".htm", ".html", ".xml", ".json", ".txt", ".ttf", ".svg", ".woff", ".woff2", ".eot"};
        for (String s : ss) {
            if (uri.endsWith(s)) {
                return true;
            }
        }
        return false;
    }

    private boolean isExcludeURI(String uri) {
        for (String exclude : excludePath) {
            if (uri.startsWith(exclude)) {
                return true;
            }
        }
        return false;
    }

    protected String getInitParameter(FilterConfig fc, String servletParamName, String contextParamName) {
        String param = fc.getInitParameter(servletParamName);
        if (isBlank(param)) {
            param = fc.getServletContext().getInitParameter(contextParamName);
        }
        return isBlank(param) ? null : param.trim();
    }

    protected boolean isBlank(String s) {
        return s == null || s.trim().length() == 0;
    }
}